#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "debug.h"

#include <sstream>
#include <string>
#include <cstdlib>
#include <QDebug>
#include <QNetworkDatagram>

#include <QRect>
#include <QPainter>
#include <QMatrix>
#include <QBuffer>
#include <QByteArray>

#include <QVideoSurfaceFormat>
#include <QActionGroup>

Q_DECLARE_METATYPE(QCameraInfo)

MainWindow::MainWindow(QWidget *parent , QScreen *screen)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    paint = false ;
    this->screen = screen ;
    sender = new QUdpSocket ;
#ifdef DEBUG
    receiverAddr = QHostAddress( "192.168.137.116" ) ;
    receiverPort = 51023 ;
#endif

    int screenAvailableSizeHeight = screen->availableSize().height() ;
    scalar = screenAvailableSizeHeight * 0.01 ; // 定义可用区域高度的最小度量大小
    ui->parameterEdit->setMaximumHeight( screenAvailableSizeHeight * 0.2 ) ;
    ui->videoWidget->setMaximumHeight( screenAvailableSizeHeight * 0.75 ) ;

    // TooBar初始化
    tooBarActions.push_back( ui->toolBar->addAction( "commit" ) ) ;
    ui->toolBar->addSeparator() ;
    tooBarActions.push_back( ui->toolBar->addAction( "start" ) ) ;
    ui->toolBar->addSeparator() ;
    tooBarActions.push_back( ui->toolBar->addAction( "stop" ) ) ;
    ui->toolBar->addSeparator() ;
    tooBarActions.push_back( ui->toolBar->addAction( "show" ) ) ;
#ifdef DEBUG
    debug = new Debug( ui->parameterEdit ) ;
    ui->toolBar->addSeparator() ;
    tooBarActions.push_back( ui->toolBar->addAction( "debug" ) ) ;
    connect( tooBarActions[4] , &QAction::triggered , [=](){ this->debug->writeQWidget() ; } ) ;
#endif
    ui->statusbar->showMessage( "暂无目标" ) ;

    // 将相机设备信息添加到菜单选项，同一时刻只有一个选项被激活
    QList<QCameraInfo> availableDevices = QCameraInfo::availableCameras() ;
    QActionGroup *devicesGroup = new QActionGroup(this) ;
    devicesGroup->setEnabled(true) ;
    for ( auto &info : availableDevices ) {
        QAction *action = new QAction( info.description() , devicesGroup ) ;
        action->setCheckable(true) ;
        action->setData( QVariant::fromValue(info) ) ;
        if ( info == QCameraInfo::defaultCamera() )
            action->setChecked(true) ;
        ui->menuDevices->addAction( action ) ;
    }
    setCamera( QCameraInfo::defaultCamera() ) ;

    connect( devicesGroup , &QActionGroup::triggered , this , &MainWindow::updateCamera ) ;

#ifdef SEND_TEST
    connect( sender , &QUdpSocket::readyRead , this , &MainWindow::readPendingDatagrams ) ;
#else
    connect( &videoSurface , &MyVideoSurface::availableFrame , this , &MainWindow::showVideoFrame ) ;
#endif

    // connect the triggered of QAction
    connect( tooBarActions[0] , &QAction::triggered , this , &MainWindow::parameterCommit ) ;
    connect( tooBarActions[1] , &QAction::triggered , this , &MainWindow::readySend ) ;
    connect( tooBarActions[2] , &QAction::triggered , this , &MainWindow::nonreadySend ) ;
    connect( tooBarActions[3] , &QAction::triggered , this , &MainWindow::isPaint ) ;

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::updateCamera( QAction *action ) {
    setCamera( qvariant_cast<QCameraInfo>( action->data() ) ) ;
}

void MainWindow::setCamera(const QCameraInfo &cameraInfo) {
    m_camera.reset( new QCamera(cameraInfo) ) ;
    m_camera->setViewfinder( &videoSurface ) ;
#ifdef DEPLOY_ANDROID
    m_camera->setCaptureMode( QCamera::CaptureVideo ) ;
#endif
    m_camera->start() ;
}

void MainWindow::showVideoFrame( QVideoFrame frame ) {
    img = frame.image() ;
#ifndef PAINT_EVENT
    static std::once_flag onceFlag ;
    std::call_once(onceFlag , [=](){
        QVideoSurfaceFormat format( img.size() , QVideoFrame::pixelFormatFromImageFormat( img.format() ) ) ;
        this->ui->videoview->videoSurface()->start( format ) ;
    } ) ;
    ui->videoview->videoSurface()->present( img.mirrored(true , true) ) ;
#else
    this->QWidget::update() ;
#endif
#ifdef DEBUG
    debug->writeBuffer( QString::number( sender->state() ) ) ;
    debug->writeBuffer( receiverAddr.toString() ) ;
    debug->writeBuffer( QString::number( receiverPort ) ) ;
#endif
}

#ifdef SEND_TEST
/*
    如果有数据可读
*/
void MainWindow::readPendingDatagrams() {
    while ( sender.hasPendingDatagrams() ) {
        QNetworkDatagram datagram = sender.receiveDatagram() ;
        showByteArrayToImage( datagram.data() ) ;
    }
}

void MainWindow::showByteArrayToImage( const QByteArray &barr ) {
    img.loadFromData( QByteArray::fromBase64( barr ) ) ;
    this->QWidget::update() ;
}
#endif


#ifdef PAINT_EVENT
/*
    使用绘制事件来显示图片
*/
void MainWindow::paintEvent(QPaintEvent *event) {
    if ( !paint ) return ;
    QPainter painter(this) ;
    auto &&pos = ui->stackedWidget->pos() ;
    auto &&size = ui->videoWidget->size() ;
    if ( img != QImage(nullptr) ) {
#ifdef DEPLOY_ANDROID
/*
    Android 设备获取到的图像是 -90° 旋转的，所以部署到 Android 时，需要处理一下
*/
        QMatrix matrix ;
        matrix.rotate(-90.0) ;
        img = img.transformed( matrix , Qt::FastTransformation ) ;
#endif
        painter.drawImage(
                    // pox.y() + scalar * 10 ，让图片绘制时与上面的部件有一些间隔
                    QRect( pos.x() , pos.y() + scalar * 5 , size.width() , size.height() ) ,
                    img.mirrored(true , true) ,
                    QRect( 0 , 0 , ui->videoWidget->width() , ui->videoWidget->height() ) ) ;
    }
}
#endif

// 点击提交参数时，处理 TextEdit 中的文本，提取参数。
void MainWindow::parameterCommit() {
    std::istringstream stream( ui->parameterEdit->toPlainText().toStdString() ) ;
    std::string address , port ;
    stream >> address >> port ;
    QString &&tmpaddr = QString( address.c_str() ) ;
    receiverAddr = QHostAddress( tmpaddr ) ,
    receiverPort = atoi( port.c_str() )  ;
    ui->statusbar->showMessage( "当前目标 : " + tmpaddr + ":" + QString(port.c_str()) ) ;
}

void MainWindow::isPaint() {
    paint = !paint ;
}

void MainWindow::readySend() {
    connect( &videoSurface , &MyVideoSurface::availableFrame , this , &MainWindow::sendVideoFrame ) ;
}

void MainWindow::nonreadySend() {
    disconnect( &videoSurface , &MyVideoSurface::availableFrame , this , &MainWindow::sendVideoFrame ) ;
}

void MainWindow::sendVideoFrame( QVideoFrame frame ) {
    QByteArray barr ;
    QBuffer buffer( &barr ) ;
    QImage img = frame.image().copy() ;
    img.save( &buffer , "jpg" , 0 ) ;
    barr = barr.toBase64() ;
    sender->writeDatagram( barr.data() , barr.size() , receiverAddr , receiverPort ) ;
}

